import sys
import time
import winreg, os
import subprocess
import threading
import json, traceback
import tkinter as tk
from tkinter import messagebox
from pystray import Icon, Menu, MenuItem
from PIL import Image, ImageDraw, ImageFont

CONFIG_PATH = "config.json"

DEFAULT_CONFIG = {
    "lang": "en",
    "auto_start": False,
    "refresh_interval": 10
}

# ---------- 多语言支持 ----------

DEFAULT_I18N = {
    "title": "USBIPD Manager",
    "Shared Devices":"Shared Devices",
    "Not Shared Devices":"Not Shared Devices",
    "Language":"Language",
    "Autostart":"Autostart",
    "Refresh":"Refresh",
    "Quit": "Quit"
}

def load_config():
    try:
        with open(CONFIG_PATH, "r", encoding="utf-8") as f:
            content = f.read()
            print("读取 config.json 内容如下：")
            print(content)
            config = json.loads(content)
            merged_config = {**DEFAULT_CONFIG, **config}
            return merged_config
    except Exception as e:
        print(f"加载配置失败: {e}")
        traceback.print_exc()
        print("将写入默认配置:")
        print(json.dumps(DEFAULT_CONFIG, indent=2, ensure_ascii=False))
        try:
            with open(CONFIG_PATH, "w", encoding="utf-8") as f:
                json.dump(DEFAULT_CONFIG, f, indent=2, ensure_ascii=False)
        except Exception as e2:
            print(f"尝试写入默认配置失败: {e2}")
        return DEFAULT_CONFIG

def save_config(config):
    try:
        serializable_config = {
            k: v for k, v in config.items()
            if isinstance(v, (str, int, float, bool, list, dict, type(None)))
        }
        print("准备写入 config.json 内容如下：")
        print(json.dumps(serializable_config, indent=2, ensure_ascii=False))
        with open(CONFIG_PATH, "w", encoding="utf-8") as f:
            json.dump(serializable_config, f, indent=2, ensure_ascii=False)
    except Exception as e:
        print(f"保存配置失败: {e}")
        traceback.print_exc()

def get_languages(i18n_path="i18n"):
    langs = set()
    if os.path.isdir(i18n_path):
        for fname in os.listdir(i18n_path):
            if fname.endswith(".json"):
                lang_code = os.path.splitext(fname)[0]
                langs.add(lang_code)
    langs.add("en")  # 固定添加en选项
    return sorted(langs)

def load_i18n(lang="en"):
    if lang == 'en':
        return DEFAULT_I18N.copy()
    path = f"./i18n/{lang}.json"
    if os.path.exists(path):
        try:
            with open(path, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            print("[WARN] Failed to load i18n file:", e)
    return DEFAULT_I18N.copy()

config = load_config()
current_lang = config.get("lang", "en")
i18n = load_i18n(current_lang)
print("[i18n] Current language:", current_lang)

def _(key):
    return i18n.get(key, key)

def switch_language(icon, lang):
    global current_lang, i18n, config

    new_i18n = load_i18n(lang)
    if new_i18n == DEFAULT_I18N and lang != 'en':
        print(f"[WARN] Failed to load language '{lang}', ignoring switch.")
        return

    current_lang = lang
    i18n = new_i18n

    print("切换语言前 config:", config)
    config["lang"] = lang
    print("切换语言后 config:", config)

    save_config(config)
    refresh_menu(icon)

def run_command(cmd_args, capture_output=True, text=True, check=False):
    creationflags = 0
    if sys.platform == "win32":
        creationflags = subprocess.CREATE_NO_WINDOW

    try:
        result = subprocess.run(
            cmd_args,
            capture_output=capture_output,
            text=text,
            check=check,
            encoding='utf-8',
            creationflags=creationflags
        )
        return result
    except subprocess.CalledProcessError as e:
        print(f"[ERROR] Command failed: {' '.join(cmd_args)}")
        print(e.stderr)
        return e

def get_usbipd_list():
    try:
        result = run_command(["usbipd", "list"])

        if result.returncode != 0:
            print("usbipd list failed:", result.stderr)
            return []

        lines = result.stdout.splitlines()
        devices = []
        for line in lines:
            if line.strip().startswith("ID") or not line.strip():
                continue

            status = None
            for s in ["Not shared", "Attached", "Shared"]:
                if s in line:
                    status = s.lower().replace(" ", "_")
                    desc = line.replace(s, "").strip()
                    break
            if not status:
                status = "unknown"
                desc = line.strip()

            parts = desc.split()
            if not parts:
                continue
            busid = parts[0]
            device_desc = " ".join(parts[1:])

            devices.append({
                "id": busid,
                "desc": device_desc,
                "status": status
            })
        return devices
    except Exception as e:
        print("Error executing usbipd list:", e)
        return []

def toggle_share(device_id, current_status):
    try:
        if current_status in ("shared", "attached"):
            run_command(["usbipd", "unbind", "--busid", device_id])
        else:
            run_command(["usbipd", "bind", "--busid", device_id, "--force"])
    except Exception as e:
        print(f"Failed to toggle share for {device_id}: {e}")

def show_device_info(dev):
    root = tk.Tk()
    root.withdraw()
    messagebox.showinfo(
        "USB Device Info",
        f"ID: {dev['id']}\nDesc: {dev['desc']}\nStatus: {dev['status']}"
    )
    root.destroy()

def is_autostart_enabled(app_name="USBIPDTray"):
    key = r"Software\Microsoft\Windows\CurrentVersion\Run"
    try:
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_READ) as reg_key:
            _, _ = winreg.QueryValueEx(reg_key, app_name)
            return True
    except FileNotFoundError:
        return False
    except Exception as e:
        print(f"Failed to check autostart: {e}")
        return False

def toggle_autostart(app_name="USBIPDTray", exe_path=None):
    key = r"Software\Microsoft\Windows\CurrentVersion\Run"
    if not exe_path:
        exe_path = os.path.abspath(sys.argv[0])
    try:
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_ALL_ACCESS) as reg_key:
            if is_autostart_enabled(app_name):
                winreg.DeleteValue(reg_key, app_name)
            else:
                winreg.SetValueEx(reg_key, app_name, 0, winreg.REG_SZ, exe_path)
    except Exception as e:
        print(f"Failed to toggle autostart: {e}")


def refresh_menu(icon):
    languages = get_languages()
    devices = get_usbipd_list()
    device_items = [] 

    for dev in devices:
        def make_action(d):
            return lambda: (
                toggle_share(d["id"], d["status"]),
                refresh_menu(icon)
            )
        
        def make_dev_checked(status):
            def checked(item=None):
                return status in ("shared", "attached")
            return checked

        if dev["status"] in ("shared", "attached", "not_shared",):
            print("dev:", dev)
            label = f"{dev['id']} - {dev['desc']}"
            device_items.append(MenuItem(label, make_action(dev), checked=make_dev_checked(dev["status"]), radio=True))


    def make_lang_action(lang):
        def action():
            switch_language(icon, lang)
        return action

    def make_checked(lang):
        def checked(item=None):
            return lang == current_lang
        return checked

    lang_menu_items = []
    for lang in languages:
        lang_upper = lang
        lang_menu_items.append(MenuItem(
            lang_upper,
            make_lang_action(lang),
            checked=make_checked(lang),
            radio=True
        ))

    menu_items = device_items + [
        MenuItem(_("Language"), Menu(*lang_menu_items)),
        MenuItem(_("Autostart"), lambda: (toggle_autostart(), refresh_menu(icon)), checked=lambda item: is_autostart_enabled()),
        MenuItem(_("Refresh"), lambda _: refresh_menu(icon)),
        MenuItem(_("Quit"), lambda _: icon.stop())
    ]
    
    icon.menu = Menu(*menu_items)

def get_tray_icon():
    logo_path = os.path.join(os.path.dirname(__file__), 'logo.png')
    if os.path.exists(logo_path):
        try:
            return Image.open(logo_path)
        except Exception as e:
            print(f"Failed to load logo.png: {e}")

    img = Image.new('RGBA', (64, 64), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
    try:
        font = ImageFont.truetype("arial.ttf", 24)
    except:
        font = ImageFont.load_default()
    text = "USBIP"
    bbox = draw.textbbox((0, 0), text, font=font)
    text_width = bbox[2] - bbox[0]
    text_height = bbox[3] - bbox[1]
    x = (64 - text_width) // 2
    y = (64 - text_height) // 2
    draw.text((x, y), text, fill="black", font=font)
    return img

def auto_refresh(icon, interval_seconds=None):
    if interval_seconds is None:
        interval_seconds = config.get("refresh_interval", 10)

    def refresh_loop():
        while True:
            time.sleep(interval_seconds)
            if icon.visible:
                refresh_menu(icon)
    threading.Thread(target=refresh_loop, daemon=True).start()

def run_tray():
    icon = Icon("USBIPD Tray", icon=get_tray_icon(), title=_("title"))
    refresh_menu(icon)
    auto_refresh(icon, interval_seconds=config.get("refresh_interval", 10))
    icon.run()

if __name__ == "__main__":
    threading.Thread(target=run_tray).start()
